package cn.edu.nju.ws.sview.wyge.relations.adataset.entities.qxliu.algorithms;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Set;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

import cn.edu.nju.ws.sview.cache.URIIDCache;
import cn.edu.nju.ws.sview.cache.URILabelCache;
import cn.edu.nju.ws.sview.database.DBConnectionFactory;
import cn.edu.nju.ws.sview.rdf.URIResource;
import cn.edu.nju.ws.sview.wyge.relations.adataset.entities.GetNamedEntities;
import cn.edu.nju.ws.sview.wyge.relations.adataset.entities.GetOthers;
import cn.edu.nju.ws.sview.wyge.relations.adataset.entities.HasNamedEntities;
import cn.edu.nju.ws.sview.wyge.relations.adataset.entities.HasOthers;
import cn.edu.nju.ws.sview.wyge.relations.datasets.DatasetItem;
import cn.edu.nju.ws.sview.wyge.util.HTMLEscape;
import cn.edu.nju.ws.sview.wyge.util.SubjectComparator;
import cn.edu.nju.ws.sview.wyge.util.Util;

public class ClassTree {
	ClassManager cm = null;
//	String ds_type = null;
//	Integer ds_id = -1;
	HashMap<Integer, ArrayList<Integer>> ancestors = null;// new
															// HashMap<Integer,
															// ArrayList<Integer>>();//
															// class-->refered
															// superclasses
	HashMap<Integer, ArrayList<Integer>> posterity = null;// new
															// HashMap<Integer,
															// ArrayList<Integer>>();//
															// class-->refered
															// subclasses
	HashMap<Integer, ArrayList<Integer>> tmppost = null;// new HashMap<Integer,
														// ArrayList<Integer>>();
	Document doc = null;
	
	HashMap<Integer, ArrayList<String>> assortedEntities = null;//only used for make JSON

	public static void main(String[] args) {
		new ClassTree("uri", 29696112).show();// 616575).show();
	}
	public ClassTree(ArrayList<Integer> docids){
		cm = new ClassManager(docids);
//		this.ds_type = DatasetItem.TEMP_GENRE;
//		this.ds_id = -1;
	}
	public ClassTree(String ds_type, Integer ds_id) {
		cm = new ClassManager(ds_type, ds_id);
//		this.ds_type = ds_type;
//		this.ds_id = ds_id;
	}
	public ClassTree(HashMap<Integer, String[]> classIdUriMap){
		cm = new ClassManager(classIdUriMap);
	}
	// acenstors
	private void makeInitialTreeofAncestors() {
		// System.out.println("ancestors...");
		Set<Integer> classes = cm.getClasses();// from uri or docid
		try {
			Connection conn = DBConnectionFactory.getConnection();//.getFalconConnection();
//			PreparedStatement pstmt = conn
//					.prepareStatement("select super_class_uri_id from sub_class_of where sub_class_uri_id=?");
			PreparedStatement pstmt = conn
					.prepareStatement("select superclass from subclass where subclass=?");
			ArrayList<Integer> ancestlist = null;
			Integer sc = null;
			this.ancestors = new HashMap<Integer, ArrayList<Integer>>();
			for (Integer c : classes) {
				pstmt.setInt(1, c);
				ResultSet rs = pstmt.executeQuery();
				ancestlist = new ArrayList<Integer>();
				while (rs.next()) {
					sc = rs.getInt(1);
					if (!ancestlist.contains(sc) && classes.contains(sc)) {
						ancestlist.add(sc);
					}
				}
				rs.close();
				ancestors.put(c, ancestlist);
			}// end of for_c
			pstmt.close();
			conn.close();
			// System.out.println("ancestors:"+ancestors.toString());
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// System.out.println("anc:"+this.ancestors);
	}

	// posterities
	@SuppressWarnings("unchecked")
	private void makeInitialTreeofPosterities() {
		if (this.ancestors == null) {
			makeInitialTreeofAncestors();
		}
		// System.out.println("Posterities...");
		Set<Integer> classes = cm.getClasses();// from uri or docid
		ArrayList<Integer> leaves = new ArrayList<Integer>();
		try {
			Connection conn = DBConnectionFactory.getConnection();//.getFalconConnection();
//			PreparedStatement pstmt = conn
//					.prepareStatement("select sub_class_uri_id from sub_class_of where super_class_uri_id=?");
			PreparedStatement pstmt = conn
					.prepareStatement("select subclass from subclass where superclass=?");
			ArrayList<Integer> posteritylist = null;
			Integer sc = null;
			posterity = new HashMap<Integer, ArrayList<Integer>>();
			for (Integer c : classes) {
				pstmt.setInt(1, c);
				ResultSet rs = pstmt.executeQuery();
				posteritylist = new ArrayList<Integer>();
				while (rs.next()) {
					sc = rs.getInt(1);
					if (!posteritylist.contains(sc) && classes.contains(sc)) {
						posteritylist.add(sc);
					}
				}
				rs.close();
				posterity.put(c, posteritylist);
				if (posteritylist.size() == 0) {
					leaves.add(c);
				}
			}// end of for_c
			pstmt.close();
			conn.close();

			// System.out.println("posst:"+this.posterity);

			// System.out.println("posterities1:"+posterity.toString());
			tmppost = (HashMap<Integer, ArrayList<Integer>>) posterity.clone();
			this.getParent(leaves);
			while (tmppost.keySet().size() > 0) {
				leaves = new ArrayList<Integer>();
				for (Integer p : tmppost.keySet()) {
					if (tmppost.get(p).size() == 0) {
						leaves.add(p);
					}
				}
				this.getParent(leaves);
			}
			// System.out.println("posterities2:"+posterity.toString());
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// System.out.println("posend:"+this.posterity);
		// System.out.println("ancend:"+this.ancestors);
	}

	@SuppressWarnings("unchecked")
	private void getParent(ArrayList<Integer> leaves) {
		for (Integer r : leaves) {
			tmppost.remove(r);
			// System.out.println(r+"\tp3:"+posterity);
			for (Integer p : this.tmppost.keySet()) {
				if (tmppost.get(p).contains(r)) {
					ArrayList<Integer> grandpars = ancestors.get(p);
					// System.out.println("grandpars:"+grandpars);
					for (Integer g : grandpars) {
						ArrayList<Integer> gchildren = tmppost.get(g);
						gchildren.remove(r);
						tmppost.put(g, gchildren);
						gchildren = posterity.get(g);
						gchildren.remove(r);
						posterity.put(g, gchildren);
						ArrayList<Integer> rancestors = ancestors.get(r);
						rancestors.remove(g);
						ancestors.put(r, rancestors);
					}
					ArrayList<Integer> pchileren = (ArrayList<Integer>) tmppost
							.get(p).clone();
					pchileren.remove(r);
					tmppost.put(p, pchileren);
				}
			}
		}
	}

	public ArrayList<Integer> getChildren(Integer classid) {
		if (this.posterity == null) {
			this.makeInitialTreeofPosterities();
		}
		return this.posterity.get(classid);
	}
	
	/**
	 * only for collections
	 * @param lang
	 * @return
	 * @throws NumberFormatException
	 * @throws Throwable
	 */
	public JSONArray makeJSONForEntityClasses(String lang) throws NumberFormatException, Throwable{

		this.makeInitialTreeofPosterities();

		JSONArray classes = new JSONArray();
		//add top classes
		for (Integer c : ancestors.keySet()) {
			if (ancestors.get(c).size() == 0) {//top classes
				// System.out.println(c+"：\t"+posterity.get(c));
				JSONObject obj = new JSONObject();
				JSONObject data = new JSONObject();
				
				String curi = cm.getNames().get(c)[0];
				String cname = this.getURIDisplayName(c, curi, lang);
//				data.put("title", cm.getNames().get(c)[1]);
				data.put("title", cname);
				
				JSONObject attr_a = new JSONObject();
				attr_a.put("title", curi);
				data.put("attr", attr_a);
				obj.put("data", data);
				
				JSONObject attr = new JSONObject();
				attr.put("id", c.toString());
				obj.put("attr", attr);
				
				JSONArray children = addJSONChildrenForEntityClasses(c, lang);
				if(children.size()>0){
					obj.put("children", children);
				}
				classes.add(obj);
			}
		}//end of for_c
		//add no typed named entities
		return classes;
	}
	/**
	 * called by makeJSONForEntityClasses(String lang)
	 * @param parentId
	 * @param lang
	 * @return
	 * @throws NumberFormatException
	 * @throws Throwable
	 */
	public JSONArray addJSONChildrenForEntityClasses(int parentId, String lang) throws NumberFormatException, Throwable{
		JSONArray children = new JSONArray();
		//add subclasses
		int childrennum = posterity.get(parentId).size();
		for(int i=0; i<childrennum; i++){
			JSONObject subc = new JSONObject();
			Integer sid = posterity.get(parentId).get(i);
//			System.out.println("sid::"+sid);
			JSONObject subdata = new JSONObject();
			
			String suri = cm.getNames().get(sid)[0];
			String sname = this.getURIDisplayName(sid, suri, lang);
//			data.put("title", cm.getNames().get(c)[1]);
			subdata.put("title", sname);
			
//			subdata.put("title", cm.getNames().get(sid)[1]);
			JSONObject attr_a = new JSONObject();
			attr_a.put("title", cm.getNames().get(sid)[0]);
			subdata.put("attr", attr_a);
			subc.put("data", subdata);
			
			JSONObject subattr = new JSONObject();
			subattr.put("id", sid.toString());
			subc.put("attr", subattr);
			
			JSONArray subch = addJSONChildrenForEntityClasses(sid, lang);
			if(subch.size()>0){
				subc.put("children", subch);
			}
			children.add(subc);
		}
		return children;
	}
	
	
	/**
	 * only for jsTree in ds_entity.jsp
	 * @param lang
	 * @return
	 * @throws NumberFormatException
	 * @throws Throwable
	 */
	public JSONArray makeJSONForDatasetClasses(String lang) throws NumberFormatException, Throwable{
		this.makeInitialTreeofPosterities();
		JSONArray result = new JSONArray();
//		if (HasNamedEntities.judge(ds_type, ds_id)) {
		if (HasNamedEntities.judge(cm.getDocId())) {
			JSONObject jsob_namedEntity = new JSONObject();
			if(lang.contentEquals("zh")){
				jsob_namedEntity.put("data", "命名实体");
			}else{
				jsob_namedEntity.put("data", "Named Entity");
			}
			jsob_namedEntity.put("state", "open");
			JSONObject ne_attr = new JSONObject();
			ne_attr.put("id", "0");
			jsob_namedEntity.put("attr", ne_attr);
			
			JSONArray classes = new JSONArray();
			if(this.assortedEntities == null){
//				this.assortedEntities = GetNamedEntities.assortEntities(ds_type, ds_id);
				this.assortedEntities = GetNamedEntities.assortEntities(cm.getDocId());
			}
			//add top classes
			for (Integer c : ancestors.keySet()) {
				if (ancestors.get(c).size() == 0) {//top classes
					// System.out.println(c+"：\t"+posterity.get(c));
					JSONObject obj = new JSONObject();
					JSONObject data = new JSONObject();
					
					String curi = cm.getNames().get(c)[0];
					String cname = this.getURIDisplayName(c, curi, lang);
//					data.put("title", cm.getNames().get(c)[1]);
					data.put("title", cname);
					
					JSONObject attr_a = new JSONObject();
					attr_a.put("title", curi);
					data.put("attr", attr_a);
					obj.put("data", data);
					
					JSONObject attr = new JSONObject();
					attr.put("id", c.toString());
					obj.put("attr", attr);
					
					JSONArray children = addJSONChildrenForDatasetClasses(c, lang);
					if(children.size()>0){
						obj.put("children", children);
					}
					classes.add(obj);
				}
			}//end of for_c
			//add no typed named entities
//			System.out.println("ne:"+assortedEntities.containsKey(0));
			if(assortedEntities != null && assortedEntities.containsKey(0)){
				ArrayList<String> entities = assortedEntities.get(0);
				Collections.sort(entities, new SubjectComparator());//============sort
//				System.out.println("elist:"+entities.toString());
				for(String e : entities){
					int eid = Integer.valueOf(e.substring(1));
					JSONObject sube = new JSONObject();
						JSONObject edata = new JSONObject();
						String euri = URIIDCache.getInstance().getURI(eid);
						String name = this.getURIDisplayName(eid, euri, lang);
//						String name = URILabelCache.getInstance().getLabel(eid, lang);
//						String euri = URIIDCache.getInstance().getURI(eid);
//						
//						if(name.trim().length()==0 && !lang.contentEquals("en")){
//							name = URILabelCache.getInstance().getLabel(eid, "en");
//						}
//						if (name.trim().length()==0) {
//							name = Util.getShortenName(euri);
//						}
//						//=====
//						if(name.trim().length() == 0){
//							name = URIResource.getLabel(eid, lang);
//						}
//						System.out.println("name:"+name+"|id:"+eid);
						
						edata.put("title", name);
							JSONObject eattr_a = new JSONObject();
							eattr_a.put("title", euri);
						edata.put("attr", eattr_a);
					sube.put("data", edata);
					
						JSONObject eattr = new JSONObject();
						eattr.put("id", e);
						eattr.put("class", "entityItem");
					sube.put("attr", eattr);
					
					classes.add(sube);
				}
			}
			
			jsob_namedEntity.put("children", classes);
			result.add(jsob_namedEntity);
		}
		//add Others
//		if (HasOthers.judge(ds_type, ds_id)) {
		if (HasOthers.judge(cm.getDocId())) {
			JSONObject jsob_others = new JSONObject();
			if(lang.contentEquals("zh")){
				jsob_others.put("data", "其他");
			}else{
				jsob_others.put("data", "Others");
			}
//			jsob_others.put("state", "open");
			JSONObject ot_attr = new JSONObject();
			ot_attr.put("id", "-1");
			jsob_others.put("attr", ot_attr);
			
//			JSONArray chothers = new JSONArray();
//			ArrayList<String> eothers = GetOthers.findEntityIds(cm.getDocId());
//			Collections.sort(eothers, new SubjectComparator());//============sort
//			for(int i=0; i<eothers.size(); i++){
//				JSONObject jsob_eo = new JSONObject();
//				String ecode = eothers.get(i);
//				jsob_eo.put("data", ecode);
//				JSONObject eo_attr = new JSONObject();
//				eo_attr.put("id", ecode.replace(":", "_"));
//				eo_attr.put("class", "entityItem");
//				eo_attr.put("title", "Blank Node "+ecode);
//				jsob_eo.put("attr", eo_attr);
//				chothers.add(jsob_eo);
//			}
//			jsob_others.put("children", chothers);
			result.add(jsob_others);
		}
		return result;
	}
	/**
	 * called by makeJSONForDatasetClasses(String lang)
	 * @param parentId
	 * @param lang
	 * @return
	 * @throws NumberFormatException
	 * @throws Throwable
	 */
	public JSONArray addJSONChildrenForDatasetClasses(int parentId, String lang) throws NumberFormatException, Throwable{
		JSONArray children = new JSONArray();
		//add subclasses
		int childrennum = posterity.get(parentId).size();
		for(int i=0; i<childrennum; i++){
			JSONObject subc = new JSONObject();
			Integer sid = posterity.get(parentId).get(i);
//			System.out.println("sid::"+sid);
			JSONObject subdata = new JSONObject();
			
			String suri = cm.getNames().get(sid)[0];
			String sname = this.getURIDisplayName(sid, suri, lang);
//			data.put("title", cm.getNames().get(c)[1]);
			subdata.put("title", sname);
			
//			subdata.put("title", cm.getNames().get(sid)[1]);
			JSONObject attr_a = new JSONObject();
			attr_a.put("title", cm.getNames().get(sid)[0]);
			subdata.put("attr", attr_a);
			subc.put("data", subdata);
			
			JSONObject subattr = new JSONObject();
			subattr.put("id", sid.toString());
			subc.put("attr", subattr);
			
			JSONArray subch = addJSONChildrenForDatasetClasses(sid, lang);
			if(subch.size()>0){
				subc.put("children", subch);
			}
			children.add(subc);
		}
		//add entities
		if(assortedEntities != null && assortedEntities.containsKey(parentId)){
			ArrayList<String> entities = assortedEntities.get(parentId);
			Collections.sort(entities, new SubjectComparator());//============sort
			for(String e : entities){
				int eid = Integer.valueOf(e.substring(1));
				JSONObject sube = new JSONObject();
					JSONObject edata = new JSONObject();
					String euri = URIIDCache.getInstance().getURI(eid);
					String name = this.getURIDisplayName(eid, euri, lang);
//					String name = URILabelCache.getInstance().getLabel(eid, lang);
//					String euri = URIIDCache.getInstance().getURI(eid);
//					
//					if(name.trim().length()==0 && !lang.contentEquals("en")){
//						name = URILabelCache.getInstance().getLabel(eid, "en");
//					}
//					
//					if (name.trim().length()==0) {
//						name = Util.getShortenName(euri);
//					}
//					//=====
//					if(name.trim().length() == 0){
//						name = URIResource.getLabel(eid, lang);
//					}
//					System.out.println("n:"+name);
					edata.put("title", name);
						JSONObject eattr_a = new JSONObject();
						eattr_a.put("title", euri);
					edata.put("attr", eattr_a);
				sube.put("data", edata);
				
					JSONObject eattr = new JSONObject();
					eattr.put("id", e);
					eattr.put("class", "entityItem");
				sube.put("attr", eattr);
				
				children.add(sube);
			}
		}
		return children;
	}
	private String getURIDisplayName(int uid, String uri, String lang) throws Throwable{

		String name = URILabelCache.getInstance().getLabel(uid, lang);
		
		if(name.trim().length()==0 && !lang.contentEquals("en")){
			name = URILabelCache.getInstance().getLabel(uid, "en");
		}
		
		if (name.trim().length()==0) {
			name = Util.getShortenName(uri);
		}
		//=====
		if(name.trim().length() == 0){
			name = URIResource.getLabel(uid, lang);
		}
		return name;
	}
	
	public Document makeDocument() {
		this.makeInitialTreeofPosterities();
		// System.out.println("document...");
		Document doc = DocumentHelper.createDocument();
		doc.setName("classtree");
		Element root = doc.addElement("root");
		ArrayList<Element> elements = new ArrayList<Element>();
		Element e = null;
		Element sube = null;
		for (Integer c : ancestors.keySet()) {
			if (ancestors.get(c).size() == 0) {
				// System.out.println(c+"：\t"+posterity.get(c));
				e = root.addElement("class");
				e.addElement("id").addText(c.toString());// .addAttribute("id",
															// c.toString());//.addElement("id").addText(c.toString());
				e.addElement("name").addText(cm.getNames().get(c)[1]);// .addText(c.toString());//.addAttribute("name",
																		// c.toString());//======================
				e.addElement("uri").addText(cm.getNames().get(c)[0]);
				int childrennum = posterity.get(c).size();
				for (int i = 0; i < childrennum; i++) {
					sube = e.addElement("subclass");
					Integer sid = posterity.get(c).get(i);
					sube.addElement("id").addText(sid.toString());
					sube.addElement("name").addText(cm.getNames().get(sid)[1]);
					sube.addElement("uri").addText(cm.getNames().get(sid)[0]);
					elements.add(sube);
				}
			}
		}
		for (int i = 0; i < elements.size(); i++) {
			e = elements.get(i);
			// System.out.println("e:"+e.attribute("id"));
			Integer id = Integer.valueOf(e.elementText("id"));
			int childrennum = posterity.get(id).size();
			for (int j = 0; j < childrennum; j++) {
				// System.out.println("i:"+i+"\tj:"+j);
				sube = e.addElement("subclass");
				Integer sid = posterity.get(id).get(j);
				sube.addElement("id").addText(sid.toString());
				// System.out.println("def:"+sid+"\t"+cm.getNames().get(sid));
				sube.addElement("name").addText(cm.getNames().get(sid)[1]);
				sube.addElement("uri").addText(cm.getNames().get(sid)[0]);
				elements.add(sube);
			}
		}
		// System.out.println("end");
//		if (HasNamedEntities.judge(ds_type, ds_id)) {
		if (HasNamedEntities.judge(cm.getDocId())) {
			root.addElement("named_entities");
		}
//		if (HasOthers.judge(ds_type, ds_id)) {
		if (HasOthers.judge(cm.getDocId())) {
			// System.out.println("sssss");
			root.addElement("others");
		}

		this.doc = doc;
		// System.out.println("doc:"+doc.asXML());
		return doc;
	}

	public void show() {
		if (this.doc == null) {
			this.doc = this.makeDocument();
		}
		System.out.println(doc.asXML());
	}
	/*
	 * public Document getDocument(){ Document doc =
	 * DocumentHelper.createDocument(); doc.setName("tree"); Element root =
	 * doc.addElement("root"); Element clas = root.addElement("class"); Element
	 * id = clas.addElement("id"); Element name = clas.addElement("name");
	 * 
	 * id.addText("1"); name.addText("myname");
	 * System.out.println("first:"+doc.asXML()); return doc; }
	 */

}
// http://xmlns.com/foaf/0.1/Person id=23
// http://www.w3.org/1999/02/22-rdf-syntax-ns#type id=15
// http://xmlns.com/foaf/0.1/Agent id=11219
// http://www.w3.org/2002/07/owl#Thing id=20879
// http://www.w3.org/2002/07/owl id=616575
// http://xmlns.com/foaf/spec/ id=29696112
// http://www.w3.org/2002/07/owl#DatatypeProperty id=19903
